package config

import (
	"fmt"
	"reflect"
	"sort"
	"strings"
)

var DefaultContext = NewContext()

var (
	GlobalRendererDefault = NewRendererDefault(0)
	GlobalRendererFile    = NewRendererFile(1)
	GlobalRendererEnv     = NewRendererEnv(2)
	GlobalRendererFlag    = NewRendererFlag(3)
)

func init() {
	DefaultContext.RegisterRenderer(GlobalRendererDefault)
	DefaultContext.RegisterRenderer(GlobalRendererFile)
	DefaultContext.RegisterRenderer(GlobalRendererEnv)
	DefaultContext.RegisterRenderer(GlobalRendererFlag)
}

type Context struct {
	root *ConfigDesc
	body Config

	arrRenderers []Renderer
}

func NewContext() *Context {
	return &Context{
		arrRenderers: make([]Renderer, 0),
	}
}

func (ctx *Context) RegisterRootConfig(c Config) *ConfigDesc {
	desc := NewConfigDesc()
	desc.Type = reflect.TypeOf(c).Elem()
	ctx.parseConfigToDesc(desc, c)
	ctx.root = desc
	ctx.body = c
	return desc
}

func (ctx *Context) RegisterConfig(path string, c Config) *ConfigDesc {
	desc := ctx.ensureParent(ctx.root, path)
	desc.Type = reflect.TypeOf(c).Elem()
	ctx.parseConfigToDesc(desc, c)
	return desc
}

func (ctx *Context) parseConfigToDesc(desc *ConfigDesc, c Config) {
	vt := reflect.TypeOf(c)
	for vt.Kind() == reflect.Ptr || vt.Kind() == reflect.Interface {
		vt = vt.Elem()
	}
	for i := 0; i < vt.NumField(); i++ {
		field := vt.Field(i)
		switch field.Type.Kind() {
		case reflect.Ptr, reflect.Interface:
			if field.Type.Implements(ValueTypeOfConfig) {
				sub := NewConfigDesc()
				sub.Type = field.Type.Elem()
				ctx.parseConfigToDesc(sub, reflect.New(field.Type.Elem()).Interface().(Config))
				desc.Children[strings.ToLower(field.Name)] = sub
			}
		case reflect.Map:
			valueType := field.Type.Elem()
			for valueType.Kind() == reflect.Ptr {
				valueType = valueType.Elem()
			}
			if (valueType.Kind() == reflect.Struct || valueType.Kind() == reflect.Interface) && valueType.Implements(ValueTypeOfConfig) {
				desc.ChildrenMap[strings.ToLower(field.Name)] = make(map[string]*ConfigDesc)
			}
		}
	}
}

func (ctx *Context) ensureParent(parent *ConfigDesc, path string) *ConfigDesc {
	ss := strings.SplitN(path, ".", 2)
	var field *ConfigDesc
	if strings.HasSuffix(ss[0], "]") {
		ssMap := strings.SplitN(ss[0], "[", 2)
		if len(ssMap) != 2 {
			panic("invalid path " + path)
		}
		fieldName := ssMap[0]
		index := ssMap[1][:len(ssMap[1])-1]
		child, exist := parent.ChildrenMap[fieldName]
		if !exist {
			child = make(map[string]*ConfigDesc)
			parent.ChildrenMap[fieldName] = child
		}
		sub, exist := child[index]
		if !exist {
			sub = NewConfigDesc()
			child[index] = sub
		}
		field = sub
	} else {
		child, exist := parent.Children[ss[0]]
		if !exist {
			panic("invalid path " + path)
		}
		field = child
	}
	if len(ss) == 2 {
		return ctx.ensureParent(field, ss[1])
	}
	return field
}

func (ctx *Context) RegisterRenderer(r Renderer) {
	// 删除已有的同优先级的renderer
	for index, renderer := range ctx.arrRenderers {
		if renderer.Sort() == r.Sort() {
			ctx.arrRenderers = append(ctx.arrRenderers[:index], ctx.arrRenderers[index+1:]...)
			break
		}
	}

	// 插入新的renderer
	ctx.arrRenderers = append(ctx.arrRenderers, r)

	// 排序
	sort.Slice(ctx.arrRenderers, func(i, j int) bool {
		return ctx.arrRenderers[i].Sort() < ctx.arrRenderers[j].Sort()
	})
}

func (ctx *Context) GetRender(name string) Renderer {
	for _, r := range ctx.arrRenderers {
		if r.Name() == name {
			return r
		}
	}
	return nil
}

func (ctx *Context) Root() *ConfigDesc {
	return ctx.root
}

func (ctx *Context) RenderConfig() error {
	if ctx.root == nil {
		return fmt.Errorf("root config is nil")
	}

	for _, r := range ctx.arrRenderers {
		err := ctx.Render(r)
		if err != nil {
			return err
		}
	}
	return nil
}

func (ctx *Context) Render(r Renderer) error {
	return r.Render(reflect.ValueOf(ctx.body), ctx.root)
}
